#include <assert.h>
#include <stdio.h>
#include <sysexits.h>
#include <string.h>
#include <unistd.h>

#include <xmlsec/app.h>
#include <xmlsec/dl.h>

#include <openssl/rsa.h>
#include <openssl/bn.h>
#include <openssl/pem.h>

#include "mcx.h"

void usage(int show) {
  fputs("Usage:\n", stderr);
  if(show & 1)
    fputs("\tmcx [-vv] [-k <keyfile>] balance\n", stderr);
  if(show & 2)
    fputs("\tmcx [-vv] [-k <keyfile>] history\n", stderr);
  if(show & 4)
    fputs("\tmcx keygen\n", stderr);
  if(show & 8)
    fputs("\tmcx [-k <keyfile>] drawdown <authorityfile> <currency> <amount>\n", stderr);
  exit(EX_USAGE);
}

void show_balance(gpointer key, gpointer value, gpointer data) {
  printf("%s: %s\n", (char *)key, (char *)value);
}

gint show_transaction(gpointer key, gpointer value, gpointer data) {
  struct mcx_transaction *t = value;

  printf("%s %1s%s %s (%d >> %d)\n", t->mt_time, t->mt_debit ? "-" : "",
	 t->mt_amount, t->mt_currency, t->mt_authority, t->mt_drawdown);
  return FALSE;
}

int main(int argc, char **argv) {
  int ch;
  char *keyfile = SYSCONFDIR "/key.rsa";
  
  xmlInitParser();
  assert(!xmlSecInit());
#ifndef XMLSEC_NO_CRYPTO_DYNAMIC_LOADING
  assert(!xmlSecCryptoDLLoadLibrary("openssl"));
#endif
  assert(!xmlSecCryptoAppInit(NULL));
  assert(!xmlSecCryptoInit());

  while((ch = getopt(argc, argv, "vk:")) != -1) {
    switch(ch) {
    case 'v':
      mcx_debug_set(mcx_debug_get() + 1);
      break;
    case 'k':
      keyfile = optarg;
      break;
    case '?':
      usage(-1);
      break;
    }
  }
  
  if((argc - optind) < 1)
    usage(-1);

  if(!strcmp(argv[optind], "keygen")) {
    RSA *rsa;
    BIGNUM *exp;

    rsa = RSA_new();
    exp = BN_new();
    if(!rsa || !exp) {
      fputs("Failed to allocate space for new RSA key.\n", stderr);
      exit(EX_SOFTWARE);
    }
    if(!BN_set_word(exp, 0x10001) || !RSA_generate_key_ex(rsa, 2048, exp, NULL)) {
      fputs("Failed to generate new RSA key.\n", stderr);
      exit(EX_SOFTWARE);
    }
    PEM_write_RSAPrivateKey(stdout, rsa, NULL, NULL, 0, NULL, NULL);
  } else {
    xmlSecKeyPtr key;

    key = mcx_loadkey(keyfile);
    if(!key) {
      fprintf(stderr, "No key found at %s\n", keyfile);
      exit(EX_NOINPUT);
    }

    if(!strcmp(argv[optind], "balance")) {
      GHashTable *balance;
      balance = mcx_balance(key);
      if(!balance) {
	fputs("Failed to retrieve balance.\n", stderr);
	exit(EX_SOFTWARE);
      }
      g_hash_table_foreach(balance, show_balance, NULL);
    } else if(!strcmp(argv[optind], "history")) {
      GTree *history;
    
      history = mcx_history(key);
      if(!history) {
	fputs("Failed to retrieve account history.\n", stderr);
	exit(EX_SOFTWARE);
      }
      g_tree_traverse(history, show_transaction, G_IN_ORDER, NULL);
    } else if(!strcmp(argv[optind], "drawdown")) {
      GHashTable *balance;
      xmlDocPtr auth;
      char *filename;

      if(argc - optind < 3)
	usage(8);
      filename = argv[optind + 1];
      if(!strcmp(filename, "-"))
	auth = xmlReadFd(0, "", NULL, XML_PARSE_NONET);
      else
	auth = xmlReadFile(filename, NULL, XML_PARSE_NONET);
      if(!auth) {
	fputs("Failed to parse authority\n", stderr);
	exit(EX_DATAERR);
      }
      balance = mcx_drawdown(key, auth, argv[optind + 2], argv[optind + 3]);
      if(!balance) {
	fputs("Failed to retrieve balance.\n", stderr);
	exit(EX_SOFTWARE);
      }
      g_hash_table_foreach(balance, show_balance, NULL);
    } else
      usage(-1);
  }
  return 0;
}
